CODE DOKUMENTATION GLIDERFIGHT
AUTOR: 	Jan-Sren "VD TONIC" Haas von VIRTUAL DIMENSION (www.virtualdimension.de)
E-MAIL: tonic@virtualdimension.de

Um die 10 erlaubten Zeilen des BASIC-Tenliner Contest maximal auszunutzen, macht das Listing Gebrauch von allen Befehlsabkrzungen, die BASIC erlaubt. Zudem wurde auf smtliche Leerzeichen verzichtet, um weitere Zeichen einzusparen. Da einige Zeilen dennoch die maximal erlaubte Zeichenzahl von 120 Zeichen erreichen kann das Programm nur mit POKE 82,0 im Editor eingegeben werden.
Um den Programmcode so klein zu bekommen waren einige Tricks notwendig, um Code einzusparen. Das hat z. T. zu einer Programmierpraxis gefhrt, die man in einem Programm, das nicht unter derart eingeschrnkten Bedingungen geschrieben werden muss, so nicht machen wrde/sollte. Da das auerdem erst mein zweites BASIC-Projekt am Atari ist, nach ber 25 Jahren, wre sogar vermutlich fr einen erfahrenen Coder noch weiteres Einsparpotenzial vorhanden.

VARIABLEN:
A	Datavariable 1
B	Datavariable 2
C()	Playerposition X
D()	Playerposition Y
E()	Missileposition X
F()	Missileposition Y
G()	Score
H()	Player Speed
I	Zhlvariable
J()	Missilespeed
K	Obergrenze des freien Speichers
L	Player Missile Basisadresse
M	Hilfsvariable Player-Basisadressen
N	Hilfsvariable Missilebereich
O	Hilfsvariable Playerbereich
P	Player Zhlvariable
Q	Hilfsvariable Tatschliche Playerposition
R	Hilfsvariable Tatschliche Missileposition

1GR.8:GR.1:SE.2,3,2:SE.0,0,14:DIMC(1),D(1),E(1),F(1),G(1),H(1),J(1):K=PEEK(106)-16:POKE54279,K:L=256*K:POKE559,62
GR.8 zum Initialisieren des RAM-Bereichs, in dem nachher der Player-Missile-Bereich liegt. Mit GR.1 wird dann nur ein kleiner Teil dieses Bereiches tatschlich fr den Grafikmodus benutzt. Der Vorteil ist, dass man sich dadurch das Nullen des PM-Bereiches spart. SE. 2,3,2 setzt die Farbe der Playfieldobjekte auf wei, SE.0,0,14 die Farbe des unteren Textbereiches auf rot. SETCOLOR spart auch ein paar Zeichen Code gegenber der Verwendung von POKEs. DIM ... dimensioniert alle Feldvariablen, die jeweils fr Player 0 Player 1 bentigt werden. K=Peek(106)-16, neue Speicherobergrenze abzglich des PM-Bereichs festhalten und mit POKE 54279,K festlegen (Bei Gelegenheit muss mir mal einer erklren, warum ich hier 16 Pages brauche. Sollte rechnerisch eigentlich weniger sein, aber dann gibt es PM-Fehler). L=256*K, die Speicheradresse fr den Beginn des PM-Bereichs festhalten. POKE 559,62 PM-Grafik initialisieren mit Standard Playfield und Single-Line-Resolution.

2POKE53277,3:POKE53260,15:POKE704,116:POKE705,68:H(0)=2:H(1)=-2:J(0)=15:J(1)=-15:POKE752,1:M=53248:N=768:O=1024:D(0)=20
POKE 53277,3 schaltet Player und Missiles ein. POKE 53260,15 setzt die Missilebreite fr beide Schsse auf vierfach, damit wir lange Striche als Schsse erhalten. POKE 704,116 und POKE 705,68 setzt die Farbe fr Player 0 auf blau, die fr Player 1 auf rot. H(0)=2:H(1)=-2 setzt die Geschwindigkeit fr beide Player. J(0)=15:J(1)=-15 setzt die Missile-Geschwindigkeit. Die Vorhaltung separater Variablen mit entsprechendem Vorzeichen spart sptere Code-Akrobatik und somit wieder einige Zeichen Code. POKE 752,1 schaltet den Textcursor aus. M=53248 Hilfsvariable, in dem Fall Speicheradresse des Kollisionsregisters fr Player 0. N=768 Hilfsvariable, in dem Fall Additionswert, um den Adressbereich der Missiles zu erhalten. O=1024 Hilfsvariable, in dem Fall Additionswert, um den Adressbereich des Players 0 zu erhalten. D(0)=20, Playerposition Y fr Player 0 festlegen. 

3D(1)=20:F.I=L+O+D(0)TOL+O+3+D(0):READA,B:POKEI,A:POKEI+256,B:N.I:?"BY VDTONIC":?#6;"GLIDERFIGHT":WH.PEEK(53279)<>6:WE.
D(1)=20, Playerposition Y fr Player 1 festlegen. F.I=L+O+D(0)TOL+O+3+D(0):READA,B:POKEI,A:POKEI+256,B:N.I liest die Daten fr Player 0 und 1 wechselseitig in einer FOR-NEXT-Schleife ein und schreibt sie in den entsprechenden Speicherbereich der beiden Player. Da der Bereich fr Player 1 256 Stellen oberhalb des Bereichs fr Player 0 liegt, muss dieser Wert addiert werden. ?"BY VDTONIC" schreibt den Urhebernamen in den unteren Graphics 0 Bereich. ?#6;"GLIDERFIGHT" schreibt den Spielnamen in den oberen Graphics 1 Bereich. Fr das Leerzeichen zwischen Glider und Fight fehlt mir ein Zeichen in der Zeile. WH.PEEK(53279)<>6:WE., wartet bis die START-Taste gedrckt wird.

4F.P=0TO1:POKEL+N+F(P),0:E(P)=0:N.P:SO.1,100,4,9:PA.30:SO.:CLS#6:?"BLUE:",G(0):?"RED:",G(1):C(0)=200:C(1)=50:P=0
Die Zeile ist auch Rcksprungzeile im Falle einer Kollision und initialisiert eine neue Runde. P=0TO1:POKEL+N+F(P),0:E(P)=0:N.P lscht die beiden Missiles. berflssig fr den ersten Durchgang, aber sinnvoll, um Code einzusparen. SO.1,100,4,9:PA.30 spielt einen kurzen Explosionssound auf Kanal 1.Auch berflssig fr den ersten Durchgang, aber ebenfalls sinnvoll um Code einzusparen.  SO. stoppt die Tonausgabe. CLS#6 lscht den oberen Graphics 1 Bereich. ?"BLUE:",G(0):?"RED:",G(1) gibt den Punktestand im Graphics 0 Bereich aus. C(0)=200:C(1)=50:P=0 X-Position Spieler 0 und Spieler 1 initialisieren auf die Bildschirmrnder, Spieler 0 beginnt.

5SO.2,60-H(0)*2,8,2:C(P)=C(P)-H(P):POKEM+P,C(P):POKE53278,1:IFC(0)<26:C(0)=222:C(1)=26:POS.RAND(16)+2,RAND(20):?#6;"*"
Die Zeile ist auch Rcksprungzeile im normalen Spielablauf. SO.2,60-H(0)*2,8,2 startet den Dsensound der Schiffe auf Kanal 2. Durch die Variable H erhht sich der Sound nach jeder Runde, um die schnellere Spielgeschwindigkeit akustisch zu untermalen. Der Befehl msste eigentlich nur einmal ausgefhrt werden, aber aus Platzproblemen musste er in diese Zeile und wird somit unntigerweise immer wieder ausgefhrt. Macht aber nix. C(P)=C(P)-H(P) ndert die X-Position des aktuellen Spielers um die Spielergeschwindigkeit. POKEM+P,C(P) setzt den aktuellen Spieler auf die neue Position. POKE53278,1 lscht die Kollisionsregister. Auch das wird berflssigerweise immer wieder ausgefhrt, um unntigen Code einzusparen, da das Kollisionsregister im Falle eines Schusstreffers sonst nicht schnell genug gelscht wird. IFC(0)<26:C(0)=222:C(1)=26 berprft, ob Player 0 links aus dem Bildschirm geflogen ist. Ist das der Fall ist auch Player 1 aus dem Bildschirm, da beide die gleiche Geschwindigkeit haben. Die beiden X-Positionen der Player werden in diesem Fall wieder auf die neuen Positionen am anderen Bildschirmrand gesetzt, so dass sie wieder neu ins Bild fliegen knnen. POS.RAND(16)+2,RAND(20):?#6;"*" positioniert jedesmal, wenn die Spieler einmal ber den Bildschirm geflogen sind an einer zuflligen Position einen Hindernisstern im oberen Graphics 1 Bereich.

6ENDIF:Q=L+O+P*256+D(P):IFSTICK(P)=13ANDD(P)<180:-MO.Q-3,Q,7:D(P)=D(P)+3:ENDIF:IFSTICK(P)=14ANDD(P)>10:MO.Q,Q-3,7
ENDIF schliet die Abfrage der Vorzeile, ob Player 0 den linken Spielrand erreicht hat. Q=L+O+P*256+D(P) hlt die tatschliche vertikale Position des aktuellen Players fest. Bei Player 1 wird wieder das Speicherbereichsoffset 256 addiert (1*256), bei Player 0 (0*256) nicht. IFSTICK(P)=13ANDD(P)<180 fragt ab, ob der Joystick des aktuellen Players nach unten bewegt wurde und schaut, ob der Player nicht bereits den unteren Bildschirmrand erreicht hat. Wenn nein, wird der aktuelle Player mit -MO.Q-3,Q,7 um 3 Pixel (3 Positionen im Speicher) verschoben. Da der Befehl Move nur kopiert, mssen um die alte Playerposition gleichzeitig auch zu lschen ber den gesamten Speicherbereich (4 Schiffpixel + 3 Pixel fr die neue Position), also insgesamt 7 Speicheradressen kopiert werden. Dadurch wird die alte Schiffsposition dann mit Nullen berschrieben. D(P)=D(P)+3 hlt die neue, vertikale Playerposition fest. ENDIF schliet die Abfrage, ob der Joystick nach unten bewegt wurde. IFSTICK(P)=14ANDD(P)>10:MO.Q,Q-3,7 ist die gleiche Abfrage fr die Joystickbewegung nach oben, nur dass hier der noarmale MOVE-Befehl verwendet wird, der pixelweise von der unteren Speicheradresse (obere Schiffspixel) angefangen zur hheren kopiert.

7D(P)=D(P)-3:ENDIF:IFSTRIG(P)=0ANDE(P)=0:E(P)=C(P):F(P)=D(P):ENDIF:R=L+N+F(P):IFE(P)>26ANDE(P)<222
D(P)=D(P)-3 hlt die neue, vertikale Position des aktuellen Players fest. ENDIF schliet die Abfrage, ob der Joystick nach oben bewegt wurde. IFSTRIG(P)=0ANDE(P)=0 fragt den Feuerknopf des aktuellen Players ab. Wenn dieser gedrckt ist und die X-Position der Missile E(P)=0 ist, ist die Voraussetzung zum Abfeuern einer Missile gegeben. E(P) ist dann null, wenn kein Schuss des aktuellen Players mehr unterwegs ist. E(P)=C(P):F(P)=D(P) setzt die X-Position der Missile E(P) dann auf die X-Position des aktuellen Spielers C(P) und die Y-Position der Missile F(P) auf die Y-Position des Spielers D(P). ENDIF schliet die Abfrage. R=L+N+F(P) berechnet die aktuelle Speicheradresse der Missile aus der PM-Basisadresse L, dem Additionswertt fr den Missilebereich N und der Y-Position der Missile des aktuellen Players F(P). IFE(P)>26ANDE(P)<222 prft nun, ob die X-Position der Missile im Spielbereich liegt. Wenn nicht, ist die Missile auerhalb des Bildschirms, also nicht abgefeuert oder bereits am Bildschirmrand angekommen.

8E(P)=E(P)-(H(P)+J(P)):POKER,3+P*9:POKEM+4+P,E(P):ELSE:POKER,0:E(P)=0:ENDIF:IFPEEK(M+4+P)<>0ORPEEK(M+9-P)<>0:P=P-1
E(P)=E(P)-(H(P)+J(P)) setzt die neue Missileposition, indem die Player- H(P) und die Missilegeschwindigkeit J(P) addiert werden und dann von der bisherigen Position abgezogen werden. Die Variablen H(P) und J(P) haben je nach Player ein positives oder negatives Vorzeichen. Durch die Addition kommt dann also ein negativer Wert fr den Player 0 und ein positiver Wert fr Player 1 raus, so dass die resultierende Bewegungsrichtung auf dem Bildschirm automatisch jeweils in entgegengesetzter Richtung ist, ganz ohne viele IF-Abfragen. POKER,3+P*9:POKEM+4+P,E(P) schreibt die neue Missileposition in den Speicher, wobei R die in Zeile 7 berechnete Speicheradresse fr die Y-Position der Missile ist und M+4+P den Wert fr das entsprechende Horizontalregister der Missile ergibt. In das Horizontalregister wird dann einfach die zuvor neu berechnete X-Position der Missile geschrieben. Fr die Speicheradresse fr die Y-Position mssen die entsprechenden Bits fr die Missile des Players 0 oder 1 erst berechnet werden aus 3+P*9. Fr die Missile des Players 0 wird also eine 3 gesetzt (3+0*9), fr Player 1 (3+1*9) eine 12. Der erfahrene Coder erkennt sofort, dass hier keine Abfrage stattfindet, ob beide Missiles auf der gleichen Hhe abgefeuert wurden. Dann msste nmlich eine 15 gesetzt werden, um beide darzustellen. Da die Abfrage aber zu Code-aufwndig ist, wird hier in Kauf genommen, dass die Missiles in diesem Fall immer wechselweise dargestellt werden, was zu einem leichten, aber aufgrund der hohen Missiliegeschwindigkeit verschmerzbaren Flackern fhrt. Ein Hoch auf Turbo Basic. ELSE:POKER,0:E(P)=0 deckt den Fall ab, dass die Missile bereits den Spielfeldrand erreicht hat. In dem Fall wird die Missile mit POKE R,0 gelscht und die X-Position der Missile E(P) auf Null gesetzt, also auerhalb des sichtbaren Spielfelds gelegt. ENDIF schliet die Abfrage. IFPEEK(M+4+P)<>0ORPEEK(M+9-P)<>0 prft, ob die Kollisionsregister "Aktueller Player kollidiert mit Playfield" oder "Player Missile des anderen Spielers kollidiert mit aktuellem Player" beschrieben sind, also eine Kollision stattgefunden hat. Die Berechnung fr ersteres erfolgt ber M+4+P und ergibt das entsprechende Kollisionsregister fr den aktuellen Player und M+9-P ergibt das entsprechende Kollisionsregister der Missile des jeweils anderen Spielers. Hat eine der Kollisionen stattgefunden, hat der aktuelle Player ein Problem, weil er kaputt gegangen ist. Da die Punkte dann der jeweils andere Spieler erhalten muss, wird, um weitere IF-Abfragen zu umgehen einfach mit P=P-1 im Falle von Player 0 die Playervariable auf -1 gesetzt, im Falle von Player 1 auf 0.  

9G(ABS(P))=G(ABS(P))+1:H(1)=H(1)-0.25:H(0)=ABS(H(1)):G.4:ENDIF:IFPEEK(M+12)<>0:G.4:ENDIF:IFP=0:P=1:ELSE:P=0:ENDIF
G(ABS(P))=G(ABS(P))+1 erhht den Punktestand des anderen Players um 1, indem jetzt der Betrag der Playervariable genommen wird, die ja nun 0 (wenn Player 1 kaputt gegangen ist) oder -1 (wenn Player 0 kaputt gegangen ist) enthlt. Wurde die Kollision bei Player 0 festgestellt wird somit G(1) erhht, bei Player 1 G(0). H(1)=H(1)-0.25:H(0)=ABS(H(1)) erhht anschlieend zunchst die Playergeschwindigkeit des Players 1 H(1), indem vom negativen Wert der Variable 0.25 subtrahiert wird und schreibt der Einfachheit halber diesen negativen Wert als Betrag dann auch in die Playergeschwindigkeitsvariable von Player 0. Mit G.4 wird dann in die Zeile zur Initialisierung einer neuen Runde zurckgesprungen. ENDIF schliet die Abfrage. IFPEEK(M+12)<>0 prft, ob die Spieler miteinander kollidiert sind. M+12 ergibt die Adresse des Kollisionsregisters von Player 0. Da es keine weiteren Spieler gibt reicht es dieses eine Register abzufragen. Ist das Kollisionsregister beschrieben, wird ohne Punhktezhlung mit G.4 in die Zeile zur Initialisierung einer neuen Runde gesprungen. Kamikaze bringt also keinem Spieler einen Vorteil. ENDIF schliet die Abfrage. Bevor die Spielschleife wiederholt werden kann, wird mit IFP=0:P=1:ELSE:P=0:ENDIF in die Spielervariable der Wert des nchsten Spielers geschrieben.

10IFG(0)+G(1)<16:G.5:ELSE:?#6;"BLUE:",G(0):?#6;"RED:",G(1):PA.300:RUN:ENDIF:D.3,192,30,120,103,230,254,127
IFG(0)+G(1)<16 prft dann noch, ob der Punktestand der beiden Spieler bereits 16 betrgt. Wenn nicht, wird die Spielschleife ab Zeile 5 mit G.5 neu durchlaufen, wenn doch wird mit ELSE:?#6;"BLUE:",G(0):?#6;"RED:",G(1) der Punktestand fr beide Player im oberen Graphics 1 Bereich ausgegeben, mit PA.300 einen Moment gewartet und das Programm anschlieend mit RUN neu gestartet und initalisiert. ENDIF schliet die Abfrage. D.3,192,30,120,103,230,254,127 enthlt wechselweise die dezimalen Daten fr die beiden Player. Das wechselweise Einlesen spart eine separate FOR-NEXT-Schleife. Auf dem Papier entworfen wurde das Schiff fr Player 0, das nach links schaut. Das Bitmuster ergibt die Dezimalwerte 3, 30, 103 und 254. Das Schiff ist also 4 Pixel hoch. Spiegelt man das Bild erhlt man die dazwischen liegenden Werte fr den zweiten Player. Der Datensatz wird in Zeile 3 eingelesen.